%%%-------------------------------------------------------------------
%%% @author mirahs(2892727493@qq.com)
%%% @doc
%%%     mnesia 管理模块
%%% @end
%%%-------------------------------------------------------------------
-module(db_mnesia_mod).

-export([
    init/1
]).

-include("common.hrl").
-include("mnesia.hrl").


%%%===================================================================
%%% API functions
%%%===================================================================

init(undefined) -> ok;
init(Handler) ->
    init_schema(),
    init_table(Handler),
    init_ver(Handler),
    mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity),
    ver_upgrade(Handler),
    Handler:init_after().


%%%===================================================================
%%% Internal functions
%%%===================================================================

init_schema() ->
    case mnesia:system_info(db_nodes) of
        [] ->
            mnesia:create_schema([node()]);
        _ ->
            ok
    end,
    application:start(mnesia, permanent),
    mnesia:change_table_copy_type(schema, node(), disc_copies).

init_table(Handler) ->
    Tables = Handler:get_tables(),
    Fun = fun
              (Table = #mnesia_table{table = Tab}) ->
                  R = create_table(Table),
                  ?INFO("init table=~w,result=~w", [Tab, R]),
                  ok
          end,
    lists:foreach(Fun, Tables),
    ok.

init_ver(Handler) ->
    case db_mnesia_info:get(mnesia_db_ver) of
        undefined ->
            db_mnesia_info:set(mnesia_db_ver, Handler:get_ver());
        {error, Reason} ->
            erlang:throw({error, Reason});
        _ ->
            ok
    end.

ver_upgrade(Handler) ->
    Handler:ver_upgrade_before(),
    ver_upgrade_special(Handler),

    case db_mnesia_info:get(mnesia_db_ver) of
        Ver when is_integer(Ver) ->
            ver_upgrade(Handler, Ver, Handler:get_ver());
        _ ->
            erlang:throw({error, "undefine mnesia_db_ver"})
    end,
    ok.

%% 扫描有子元素版本管理的 table, 并做版本转换
ver_upgrade_special(Handler) ->
    ver_upgrade_special2(Handler:get_tables()).

ver_upgrade_special2([]) -> ok;
ver_upgrade_special2([#mnesia_table{parse = undefined} | T]) ->
    ver_upgrade_special2(T);
ver_upgrade_special2([Mt = #mnesia_table{parse = Parse} | T]) ->
    case Parse:get_field_parse() of
        [] ->
            ver_upgrade_special2(T);
        _ ->
            do_parse_table(Mt),
            ver_upgrade_special2(T)
    end.

ver_upgrade(_Handler, StartVer, EndVer) when StartVer >= EndVer ->
    ok;
ver_upgrade(Handler, StartVer, EndVer) ->
    ?INFO("升级 mnesia_db, 版本号: ~w", [StartVer]),
    Handler:ver_upgrade(StartVer),
    ?INFO("完成升级 mnesia_db, 版本号: ~w", [StartVer]),
    db_mnesia_info:set(mnesia_db_ver, StartVer + 1),
    ver_upgrade(Handler, StartVer + 1, EndVer).


create_table(#mnesia_table{table = Tab, record_name = Rec, attributes = Fields, copies = Copies, index = Index}) ->
    mnesia:create_table(Tab, [{Copies, [erlang:node()]}, {record_name, Rec}, {type, set}, {attributes, Fields}, {index, Index}]).

do_parse_table(#mnesia_table{table = Tab, parse = Parse, attributes = NewAttributes, record_name = NewRecordName}) ->
    ?INFO("mnesia_table 数据版本转换: ~w", [Tab]),
    mnesia:transform_table(Tab, fun(R) -> do_parse_record(R, Parse) end, NewAttributes, NewRecordName),
    ok.

do_parse_record(Data, Parse) ->
    case parse:parse(Parse, Data) of
        {ok, NewData} -> NewData;
        {false, Reason} ->
            erlang:throw({error, Reason})
    end.
